package validate

import (
	"fmt"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	enTranslations "github.com/go-playground/validator/v10/translations/en"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
	"gspeed/app/dist"
	validate "gspeed/app/http/validator"
	"gspeed/bootstrap/global"
	"reflect"
	"regexp"
	"strings"
)

type validI18n struct {
	uni       *ut.UniversalTranslator
	validator *validator.Validate
}

var Validate validI18n

// 初始化
func (v *validI18n) Init() {
	// A.从配置文件获取设置的语言
	// 配置默认语言为 英语
	global.Config.SetDefault("app.locale", "en")
	// 获取 配置的语言
	language := global.Config.GetString("app.locale")

	// B.实例化 Validator
	if engine, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.validator = engine
		// 1.实例语言
		zhPack := zh.New() //中文语言包
		enPack := en.New() //英文语言包
		// 2.根据语言，实例多语言的翻译器（第一个参数是默认语言，其余是支持的语言）
		v.uni = ut.New(enPack, zhPack, enPack)
		// 3.获取指定语言的翻译器
		v.getTrans(language)

		for tag, fn := range validate.RegValidator() {
			// 注册自定义函数
			go v.RegisterValidation(tag, fn)
		}

		//注册一个函数，获取struct tag里自定义的label作为字段名
		v.customTagName("label")
	}
	return
}

// 获取对应语言的翻译器
func (v *validI18n) getTrans(lang string) (trans ut.Translator) {
	validTransKey := "validTrans_" + lang
	getTrans := global.Container.Get(validTransKey)
	if getTrans == nil {
		var ok bool
		trans, ok = v.uni.GetTranslator(lang)
		if !ok {
			fmt.Errorf("uni.GetTranslator(%s) failed", lang)
			return nil
		}
		// 设置对应的语言翻译器
		v.setLanguageTrans(lang, trans)
		for tag, _ := range dist.Trans.ValidMsgMap(lang) {
			// 注册对应tag的翻译
			go v.RegisterTranslate(tag, trans)
		}
		global.Container.Set(validTransKey, trans)
	} else {
		trans = getTrans.(ut.Translator)
	}
	return trans
}

// 设置语言翻译器
func (v *validI18n) setLanguageTrans(lang string, trans ut.Translator) {
	switch lang {
	case "en":
		enTranslations.RegisterDefaultTranslations(v.validator, trans)
	case "zh":
		zhTranslations.RegisterDefaultTranslations(v.validator, trans)
	default:
		enTranslations.RegisterDefaultTranslations(v.validator, trans)
	}
	return
}

// 注册自定义验证器
func (v *validI18n) RegisterValidation(tag string, fn validator.Func) {
	if tag != "" && fn != nil {
		err := v.validator.RegisterValidation(tag, fn)
		if err != nil {
			fmt.Errorf("注册自定义验证器出错：%s", err.Error())
		}
	}
	return
}

// 注册自定义翻译
func (v *validI18n) RegisterTranslate(tag string, trans ut.Translator) {
	//根据提供的标记注册翻译
	if tag != "" {
		err := v.validator.RegisterTranslation(tag, trans, func(ut ut.Translator) error {
			return ut.Add(tag, dist.Trans.ValidMsg(tag, trans.Locale()), true)
		}, func(ut ut.Translator, fe validator.FieldError) string {
			t, _ := ut.T(tag, fe.Field(), fe.Field())
			return t
		})
		if err != nil {
			fmt.Errorf("注册自定义翻译出错：%s", err.Error())
		}
	}

	return
}

// 自定义标签名
func (v *validI18n) customTagName(key string) {
	if key != "" {
		// 仅且一次，扫描所有tag的值，以后再调用这个方法不会改变第一次的扫描
		v.validator.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := fld.Tag.Get(key)
			return "{{." + name + "}}"
		})
	}

	return
}

// 翻译 validator 错误
func (v *validI18n) Translate(err interface{}, lang string) interface{} {
	// 获取validator.ValidationErrors类型的errors
	validErrs, ok := err.(validator.ValidationErrors)
	if !ok {
		// 非validator.ValidationErrors类型错误直接返回
		return err
	}
	// validator.ValidationErrors类型错误则进行翻译
	errs := validErrs.Translate(v.getTrans(lang))
	result := make([]string, 0, len(errs))
	for _, v := range errs {
		result = append(result, v)
	}
	return v.transTag(result[0], lang)
}

// 把字段名翻译
func (v *validI18n) transTag(message string, lang string) string {
	rex := regexp.MustCompile(`{{.(.*?)}}`)
	out := rex.FindAllStringSubmatch(message, -1)
	for _, subStr := range out {
		transStr := dist.Trans.LabelText(subStr[1], lang)
		message = strings.Replace(message, subStr[0], transStr, -1)
	}
	return message
}
